﻿using System;
using System.ComponentModel;
using System.Globalization;
using System.Text;

namespace Kyoh.Lib.IO.Archive
{
    /// <summary><see cref="ZipEncoding"/> オブジェクトの相互変換をサポートする <see cref="TypeConverter"/> の実装。</summary>
    public class ZipEncodingTypeConverter : TypeConverter
    {
        /// <summary><see cref="ZipEncodingTypeConverter"/> オブジェクトが、指定された型から <see cref="ZipEncoding"/> オブジェクトへの変換をサポートするかどうかを取得します。</summary>
        /// <param name="context">型変換のコンテキストを表す <see cref="ITypeDescriptorContext"/> 。</param>
        /// <param name="sourceType">変換元の型を表す <see cref="Type"/>。</param>
        /// <returns>指定された型からの変換が可能な場合には <c>true</c>。それ以外の場合は <c>false</c>。</returns>
        public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
        {
            if (sourceType == typeof(ZipEncoding) ||
                sourceType == typeof(Encoding) ||
                sourceType == typeof(int) ||
                sourceType == typeof(string))
                return true;
            else
                return false;
        }

        /// <summary><see cref="ZipEncodingTypeConverter"/> オブジェクトが、指定された型への変換をサポートするかどうかを取得します。</summary>
        /// <param name="context">型変換のコンテキストを表す <see cref="ITypeDescriptorContext"/> 。</param>
        /// <param name="destinationType">変換先の型を表す <see cref="Type"/>。</param>
        /// <returns>指定された型への変換が可能な場合には <c>true</c>。それ以外の場合は <c>false</c>。</returns>
        public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
        {
            if (destinationType == typeof(string) ||
                destinationType == typeof(ZipEncoding))
                return true;
            else
                return false;
        }

        // サポートされていない変換
        private ZipEncoding notSupportType()
        {
            throw new NotSupportedException();
        }

        /// <summary>対応するオブジェクトから、<see cref="ZipEncoding"/> オブジェクトへの変換を行います。</summary>
        /// <param name="context">型変換のコンテキストを表す <see cref="ITypeDescriptorContext"/> 。</param>
        /// <param name="culture">現在のカルチャとして使用する <see cref="CultureInfo"/>。</param>
        /// <param name="value">変換元のオブジェクト。</param>
        /// <returns>変換結果の <see cref="ZipEncoding"/>。</returns>
        public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
        {
            return (value == null)
                 ? null
                 : value as ZipEncoding
                      ?? convertFromEnc(value as Encoding)
                      ?? convertFromNum(value as int?)
                      ?? convertFromStr(value as string)
                      ?? notSupportType();
        }

        // int→ZipDecoder変換
        private object convertFromNum(int? numValue)
        {
            return numValue.HasValue
                 ? new ZipEncoding(Encoding.GetEncoding(numValue.Value))
                 : null;
        }

        // string→ZipDecoder変換
        private object convertFromStr(string strValue)
        {
            return strValue != null
                ? (strValue.StartsWith(ZipEncoding.Utf8HeaderString)
                    ? new ZipEncoding(Encoding.GetEncoding(strValue.Substring(ZipEncoding.Utf8HeaderLength)), true)
                    : new ZipEncoding(Encoding.GetEncoding(strValue), false))
                : null;
        }

        // Encoding→ZipDecoder変換
        private object convertFromEnc(Encoding encValue)
        {
            return encValue == null
                ? null
                : new ZipEncoding(encValue);
        }

        /// <summary>対応するオブジェクトを、指定された型のオブジェクトへの変換を行います。</summary>
        /// <param name="context">型変換のコンテキストを表す <see cref="ITypeDescriptorContext"/> 。</param>
        /// <param name="culture">現在のカルチャとして使用する <see cref="CultureInfo"/>。</param>
        /// <param name="value">変換元のオブジェクト。</param>
        /// <param name="destinationType">変換先の型を表す <see cref="Type"/>。</param>
        /// <returns>変換結果のオブジェクト。</returns>
        public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
        {
            return destinationType == typeof(string)
                ? value as string
                    ?? convertToStr(value as ZipEncoding)
                    ?? notSupportType()
                : destinationType == typeof(ZipEncoding)
                    ? value as ZipEncoding
                        ?? convertFromEnc(value as Encoding)
                        ?? convertFromNum(value as int?)
                        ?? convertFromStr(value as string)
                        ?? notSupportType()
                    : notSupportType();
        }

        // ZipDecoder→string変換
        private object convertToStr(ZipEncoding zipValue)
        {
            return zipValue == null
                ? null
                : zipValue.ToString();
        }
    }

}
